// Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "global.h"
#include "error.h"
#include "wimax.h"
#include "wm_ioctl.h"
#include "io.h"
#include "device.h"
#include "hci.h"
#include "log.h"
#include "fload.h"

typedef const char * ld_file_t;

typedef struct {
	void *p;
	int size;
} ld_buf_t;

typedef struct {
	bool	is_file;
	union {
		ld_file_t path;
		ld_buf_t buf;
	} u;
} ld_dst_t;

int bl_upload(int dev_idx, int type, void *buf, int size, void (*progress)(int bytes))
{
	xprintf(SDK_ERR, "%s: not supported!\n", __func__);
		return -1;
}

int bl_download(int dev_idx, int type, void *buf, int size, void (*progress)(int bytes))
{
	xprintf(SDK_ERR, "%s: not supported!\n", __func__);
		return -1;
}

static int do_read_file(int dev_idx, const char *target_file,
		ld_dst_t *dst, void (*progress)(int bytes))
{
	device_t *dev;
	u8 send_buf[HCI_MAX_PACKET];
	u8 recv_buf[HCI_MAX_PACKET];
	hci_file_read_t *file = (hci_file_read_t *) send_buf;
	hci_file_response_t *rsp;
	u32 offset = 0;
	int fd = 0;
	int ret = -1, len, max_size = 0;
	char *p = NULL;
	u16 cmd;
	void *param;
	int plen;

	if (!(dev = dm_get_dev(dev_idx)))
		return -1;

	xfunc_in("[%d]", dev_idx);

	if (dst->is_file) {
		if ((fd = open(dst->u.path, O_CREAT|O_WRONLY|O_TRUNC, 0644)) < 0) {
			xprintf(SDK_STD_ERR, "open(%s) fail\n", dst->u.path);
			goto out;
		}
	}
	else {
		if (!dst->u.buf.p || !dst->u.buf.size) {
			xprintf(SDK_ERR, "buf=%p, size=%d\n", dst->u.buf.p, dst->u.buf.size);
			goto out;
		}
		p = dst->u.buf.p;
		max_size = dst->u.buf.size;
	}

	strcpy((char *)file->path, target_file);
	cmd = WIMAX_READ_FILE;
	param = file;
	plen = sizeof(hci_file_read_t) + strlen((char *)file->path) + 1/*null*/;

	pthread_mutex_lock(&dev->load_lock);
	while (1) {
		file->offset = DH2B(offset);
		ret = hci_send_wait(dev_idx, cmd, param, plen,
				WIMAX_FILE_RESULT, recv_buf, sizeof(recv_buf), 0, 0, 3);
		if (ret < 0)
			break;

		rsp = (hci_file_response_t *) recv_buf;
		if (!(len = DB2H(rsp->result))) {
			ret = 0;
			break;
		}
		if (len < 0) {
			xprintf(SDK_ERR, "Read file failed(%d)\n", ret);
			ret = -1;
			break;
		}

		if (dst->is_file) {
			ret = write(fd, file_response_data(rsp), len);
			if (ret <= 0) {
				xprintf(SDK_ERR, "Write fail %s(%d!=%d)\n", dst->u.path, ret, len);
				ret = -1;
				break;
			}
		}
		else {
			if (offset + len > max_size) {
				xprintf(SDK_ERR, "buffer is too small(%d > %d)\n",
					offset + len, max_size);
				ret = -1;
				break;
			}
			memcpy(p+offset, file_response_data(rsp), len);
		}

		offset += len;
		if (progress)
			progress(offset);
	}
	pthread_mutex_unlock(&dev->load_lock);

out:
	if (fd > 0)
		close(fd);
	xprintf(SDK_INFO, "Read file size=%d\n", offset);
	if (!ret) {
		if (!offset)
			ret = -ENOENT;
		else
			ret = offset;
	}

	xfunc_out("ret=%d", ret);
	dm_put_dev(dev_idx);
	return ret;
}

int fl_read_file(int dev_idx, const char *target_file,
		const char *host_file, void (*progress)(int bytes))
{
	ld_dst_t dst;
	int ret;
	
	dst.is_file = TRUE;
	dst.u.path = host_file;
	ret = do_read_file(dev_idx, target_file, &dst, progress);

	return ret;
}
